using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace ReactiveCode.Linq
{
    using Util;

    interface IOrderedObservableEnumerable<TElement>: IObservableEnumerable<TElement>
    {
        IOrderedObservableEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
    }

    interface IObservableGrouping<TKey, TElement>: IObservableEnumerable<TElement>
    {
        TKey Key { get; }
    }

    partial class ObservableEnumerable
    {
        public static IEnumerable<T> AsEnumerable<T>(this IObservableEnumerable<T> source)
        {
            return EnumerableUtil.Defer(() =>
            {
                var latest = source.LatestContinuous();
                latest.Dispose();
                return latest.Select(x => x.Last());
            });
        }

        static IObservableEnumerable<TResult> ApplyUnaryOp<TSource, TResult>(this IObservableEnumerable<TSource> source, Func<IEnumerableObserver<TResult>, UnaryOperator<TSource, TResult>> opFunc)
        {
            return Create<TResult>(o =>
            {
                var op = opFunc(o);
                var subscription = source.Subscribe(op);
                return delegate
                {
                    subscription.Dispose();
                    op.Dispose();
                };
            });
        }

        public static IObservableEnumerable<T> Where<T>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<bool>> predicate)
        {
            Contract.Requires(source != null && predicate != null);
            return source.ApplyUnaryOp<T, T>(o => new Selection<T>(o, predicate));
        }
        internal static IObservableEnumerable<T> TakeWhile<T>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<bool>> predicate)
        {
            throw new NotImplementedException();
        }
        internal static IObservableEnumerable<T> SkipWhile<T>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<bool>> predicate)
        {
            throw new NotImplementedException();
        }

        public static IObservableEnumerable<TResult> Select<TSource, TResult>(this IObservableEnumerable<TSource> source, Func<IObservable<TSource>, IObservable<TResult>> resultSelector)
        {
            Contract.Requires(source != null && resultSelector != null);
            return source.ApplyUnaryOp<TSource, TResult>(o => new Projection<TSource, TResult>(o, resultSelector));
        }

        internal static IObservableEnumerable<TResult> SelectMany<TSource, TResult>(this IObservableEnumerable<TSource> source, Func<IObservable<TSource>, IObservableEnumerable<TResult>> selector)
        {
            throw new NotImplementedException();
        }

        internal static IObservableEnumerable<TOuter> Join<TOuter, TInner, TKey, TResult>(this IObservableEnumerable<TOuter> outer, IObservableEnumerable<TInner> inner,
            Func<IObservable<TOuter>, IObservable<TKey>> outerKeySelector, Func<TInner, IObservable<TKey>> innerKeySelector,
            Func<IObservable<TInner>, IObservable<TOuter>, IObservable<TResult>> resultSelector)
        {
            throw new NotImplementedException();
        }

        internal static IObservableEnumerable<TFirst> Zip<TFirst, TSecond, TResult>(this IObservableEnumerable<TFirst> first, IObservableEnumerable<TSecond> second,
            Func<IObservable<TSecond>, IObservable<TFirst>, IObservable<TResult>> resultSelector)
        {
            throw new NotImplementedException();
        }

        internal static IOrderedObservableEnumerable<T> OrderBy<T, TKey>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<TKey>> keySelector)
        {
            throw new NotImplementedException();
        }
        internal static IOrderedObservableEnumerable<T> OrderByDescending<T, TKey>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<TKey>> keySelector)
        {
            throw new NotImplementedException();
        }
        internal static IOrderedObservableEnumerable<T> ThenBy<T, TKey>(this IOrderedObservableEnumerable<T> source, Func<IObservable<T>, IObservable<TKey>> keySelector)
        {
            throw new NotImplementedException();
        }
        internal static IOrderedObservableEnumerable<T> ThenByDescending<T, TKey>(this IOrderedObservableEnumerable<T> source, Func<IObservable<T>, IObservable<TKey>> keySelector)
        {
            throw new NotImplementedException();
        }

        internal static IObservableEnumerable<IObservableGrouping<TKey, T>> GroupBy<TKey, T>(this IObservableEnumerable<T> source, Func<IObservable<T>, IObservable<TKey>> keySelector)
        {
            throw new NotImplementedException();
        }

        internal static IObservableEnumerable<T> Union<T>(this IObservableEnumerable<T> first, IObservableEnumerable<T> second, IEqualityComparer<T> comparer)
        {
            throw new NotImplementedException();
        }
        internal static IObservableEnumerable<T> Concat<T>(this IObservableEnumerable<T> first, IObservableEnumerable<T> second)
        {
            throw new NotImplementedException();
        }
        internal static IObservableEnumerable<T> Except<T>(this IObservableEnumerable<T> first, IObservableEnumerable<T> second, IEqualityComparer<T> comparer)
        {
            throw new NotImplementedException();
        }
        internal static IObservableEnumerable<T> Intersect<T>(this IObservableEnumerable<T> first, IObservableEnumerable<T> second, IEqualityComparer<T> comparer)
        {
            throw new NotImplementedException();
        }
    }
}